#include "MainForm.h"

using namespace System;
using namespace Core;
using namespace std;


namespace Chess {

	void MainForm::PrepareBoard()
	{
		turnTimer->Enabled = true;
		_turnTick = DateTime::Now;

		for (int r = 0; r < Chessboard::RANKS; r++)
			for (int c = 0; c < Chessboard::COLUMNS; c++)
			{
				Piece * p = Engine::instance()->chessboard()->at(r, c)->piece();
				_chessPanels[r, c]->BackgroundImage = p == NULL ? nullptr :
						Image::FromFile(_imagePaths[p->key()]);
			}

		// display pieces
	}

	void MainForm::Finish()
	{
		turnTimer->Enabled = false;
		_finished = true;

		lblStatus->Text = "The game is drawn!";
		MessageBox::Show(lblStatus->Text);
		HideValidMoves();
	}


	void MainForm::New(int mode, int time)
	{
		_finished = false;
		_started = true;

		Engine::humans(mode);
		_timeLimitMinutes = time;
		lblTimeLimit->Text = "Limit: " + (_timeLimitMinutes == 0 ? "-" : _timeLimitMinutes + " mins");
		turnTimer->Enabled = false;

		_whiteTime = TimeSpan::Zero;
		_blackTime = TimeSpan::Zero;

		saveToolStripMenuItem->Enabled = true;
		undoToolStripMenuItem->Enabled = true;
		redoToolStripMenuItem->Enabled = true;

		Engine::reset();

		HideValidMoves();
		// remove the pieces from board
		for (int r = 0; r < Chessboard::RANKS; r++)
			for (int c = 0; c < Chessboard::COLUMNS; c++)
			{
				_chessPanels[r, c]->BackgroundImage = nullptr;
				//_chessPanels(r, c)->Invalidate();
			}
	}

	string ToBasicString(String^ str)
	{
		int length = str->Length;
		char* toChar = new char[length+1];
		for(int i=0; i < length; i++)
			toChar[i] = str[i];
		toChar[length] = NULL;
		string retStr = toChar;
		return retStr;
	}


	void MainForm::LoadGame()
	{
		if(openFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK)
		{
			
			System::Collections::Generic::IEnumerable<String^> ^ gameStr = 
				System::IO::File::ReadLines(openFileDialog1->FileName);

			int i =0;
			for each (String^ str in gameStr)
			{
				if(i == 0)
					Engine::humans(Convert::ToInt32(str));
				else if(i == 1)
					_timeLimitMinutes = Convert::ToUInt32(str);
				else if(i == 2)
				{
					New(Engine::humans(), _timeLimitMinutes);
					Engine::loadFromString(ToBasicString(str));
				}
				else if(i == 3)
					_whiteTime = TimeSpan::FromMilliseconds(Convert::ToDouble(str));
				else if(i == 4)
					_blackTime = TimeSpan::FromMilliseconds(Convert::ToDouble(str));
				i++;
			}

			saveToolStripMenuItem->Enabled = true;
			undoToolStripMenuItem->Enabled = true;
			redoToolStripMenuItem->Enabled = true;

			PrepareBoard();
			lblStatus->Text = "Game loaded";
		}
	}

	void MainForm::SaveGame()
	{
		if(saveFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK)
		{
			
			string s = Engine::instance()->saveToString();

			String ^ str = Engine::humans().ToString() + Environment::NewLine + 
				_timeLimitMinutes.ToString() + Environment::NewLine + 
				gcnew String(s.c_str()) + Environment::NewLine +
				_whiteTime.TotalMilliseconds.ToString() + Environment::NewLine + 
				_blackTime.TotalMilliseconds.ToString();

			System::IO::File::WriteAllText(saveFileDialog1->FileName, str);

			lblStatus->Text = "Game saved";
		}
	}

	Square * MainForm::GetSquareAt(int row, int col)
	{
		return Engine::instance()->chessboard()->at(row, col);
	}

	Square * MainForm::GetSquareByKey(int key)
	{
		int col = key % 10;
		int row = (key - col) / 10;
		return Engine::instance()->chessboard()->at(row, col);
	}

	void MainForm::TurnChanged()
	{
		lblBlackTime->Font = gcnew System::Drawing::Font(lblBlackTime->Font->FontFamily, lblBlackTime->Font->Size, 
			BlacksTurn() ? FontStyle::Bold : FontStyle::Regular);
		lblWhiteTime->Font = gcnew System::Drawing::Font(lblWhiteTime->Font->FontFamily, lblWhiteTime->Font->Size, 
			WhitesTurn() ? FontStyle::Bold : FontStyle::Regular);
	}

	void MainForm::PanelSelected(ChessPanel^ pnl)
	{
		int row = pnl->row();
		int col = pnl->column();
		Square * sq = GetSquareAt(row, col);

		if(_selectedPanel != nullptr && (sq->piece() == NULL || sq->piece()->color() != Engine::instance()->whosPlaying()))
		{
			// if a valid move selected
			if(_validPanels->ContainsKey(sq->key()))
				PerformMove(sq->row(), sq->column());
			else
				lblStatus->Text = "Cannot move there!";
		}
		else if(sq->piece() != NULL && sq->piece()->color() == Engine::instance()->whosPlaying())
		{
			//if(_selectedPanel != nullptr && (_selectedPanel->row() != pnl->row() || _selectedPanel->column() != pnl->column()))
			if(_selectedPanel != pnl)
			{
				HideValidMoves();
				_selectedPanel = pnl;
				ShowValidMoves();
			}
		}
	}

	void MainForm::HideValidMoves()
	{
		if(_selectedPanel != nullptr)
		{
			_selectedPanel->BorderWidth(0);
			_selectedPanel->Invalidate();
			_selectedPanel = nullptr;
		}

		for each (KeyValuePair<int, ChessPanel^> kv in _validPanels)
		{
			kv.Value->BorderWidth(0);
			kv.Value->Invalidate();
		}

		_validPanels->Clear();
	}

	void MainForm::ShowValidMoves()
	{
		// color the selected panel border
		_selectedPanel->BorderWidth(2);
		_selectedPanel->Invalidate();
		Square * sq = GetSquareAt(_selectedPanel->row(), _selectedPanel->column());
		
		map<int, Core::Move*> &validMoves = sq->piece()->validMoves();

		for (map<int, Core::Move*>::iterator it = validMoves.begin(); it != validMoves.end(); it++)
		{
			Square * dst = it->second->to();
			ChessPanel^ pnl = _chessPanels[dst->row(), dst->column()];
			_validPanels->Add(dst->key(), pnl);
			
			// color the panel border
			pnl->BorderWidth(2);
			pnl->Invalidate();
		}
	}

	void MainForm::PerformMove(int row, int col)
	{
		try
		{
			lblStatus->Text = "";

			Engine::instance()->move(_selectedPanel->row(), _selectedPanel->column(), row, col);

			const Core::Move * mv = Engine::instance()->lastMove();
			
//			_selectedPanel->BackgroundImage = nullptr;

			UpdateMove(mv);
			ConsiderOverallStatus();

			// remove colored panels
			HideValidMoves();

			// if next move is computer
			if(Engine::humans() == 1)
			{
				Engine::instance()->blackPlr()->move();
				UpdateMove(Engine::instance()->lastMove());
			}
		}
		catch(string str)
		{
			lblStatus->Text = gcnew String(str.c_str());
		}
		catch(String ^str)
		{
			lblStatus->Text = str;
		}
		catch(...)
		{
			lblStatus->Text = "Error occured...";
		}
	}

	void MainForm::UpdateMove(const Core::Move *mv)
	{
		_chessPanels[mv->from()->row(), mv->from()->column()]->BackgroundImage = nullptr;
		if(mv->action() & ChEnpassant)
		{
			Square * sq = mv->pieceEaten()->square();
			_chessPanels[sq->row(), sq->column()]->BackgroundImage = nullptr;
		}
		if(mv->action() & ChCastling)
		{
			Square * sq = mv->prevMove()->from();
			_chessPanels[sq->row(), sq->column()]->BackgroundImage = nullptr;
			_chessPanels[mv->prevMove()->to()->row(), mv->prevMove()->to()->column()]->BackgroundImage = 
				Image::FromFile(_imagePaths[mv->prevMove()->piece()->key()]);
			lblStatus->Text = WhosTurnStr() + " King castled.";
		}

		_chessPanels[mv->to()->row(), mv->to()->column()]->BackgroundImage = Image::FromFile(_imagePaths[mv->to()->piece()->key()]);

	}

	void MainForm::ConsiderOverallStatus()
	{
		if(Engine::instance()->hasCheckCondition())
		{		
			lblStatus->Text = WhosTurnStr() + " is in check!";
			MessageBox::Show("Check!");
		}
	}

	void MainForm::Undo()
	{
		if(Engine::instance()->undoMove() == NULL)
		{
			lblStatus->Text = "Cannot undo more!";
			return;
		}
		if(Engine::humans() == 1)
			Engine::instance()->undoMove();

		HideValidMoves();
		PrepareBoard();
		ConsiderOverallStatus();
	}

	void MainForm::Redo()
	{
		const Core::Move * mv = Engine::instance()->redo();

		if(mv == NULL)
		{
			lblStatus->Text = "Cannot redo more!";
			return;
		}
		if(Engine::humans() == 1)
			Engine::instance()->redo();

		HideValidMoves();
		UpdateMove(mv);
		ConsiderOverallStatus();
	}
}

